/* libwatopt++ - Optimization Toolkit
 *
 * Copyright (c) 2007 Christopher Alexander Watford
 * <christopher.watford@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * $Id: kohonen_network.hpp 2 2007-05-06 18:01:15Z christopher.watford $
 */

#if _MSC_VER > 1000
#pragma once
#endif /* _MSC_VER > 1000 */

#ifndef _KOHONEN_NETWORK_HPP_
#define _KOHONEN_NETWORK_HPP_

#include <vector>

namespace watopt
{
	typedef size_t neuronid_t;

	class IKohonenNeighborhood
	{
	public:
		virtual size_t Inputs( ) const = 0;
		virtual size_t Neurons( ) const = 0;
		virtual double operator() (const neuronid_t nn, const double* inputs) const = 0;
		virtual neuronid_t First(void) const = 0;
		virtual neuronid_t Next(const neuronid_t nn) const = 0;
		virtual neuronid_t Last(void) const = 0;
		virtual double Update (const neuronid_t nn, const neuronid_t winner, const double* inputs, double eta) = 0;
		virtual bool IsNeighbor(const neuronid_t n1, const neuronid_t n2) const = 0;
		virtual double Weighting(const neuronid_t winner, const neuronid_t node) const = 0;
      virtual size_t GetNeighborhoodSize(void) const = 0;
      virtual void SetNeighborhoodSize(size_t sz) = 0;
	};

	class KohonenNetwork
	{
	public:
		KohonenNetwork(IKohonenNeighborhood* hood)
			: m_Neighborhood(hood)
			, m_Rate(0.5)
		{
		}

		~KohonenNetwork( )
		{
		}

		double Train(const double* inputs);
      neuronid_t Categorize(const double* inputs);
		void operator() (double* inputs, std::vector<neuronid_t>& winners);
      double GetRate(void) const { return m_Rate; }
      void SetRate(double rate) { m_Rate = rate; }

	private:
		double   m_Rate;
		IKohonenNeighborhood*	m_Neighborhood;
	};

	class OneDNeighborhood : public IKohonenNeighborhood
	{
	public:
		OneDNeighborhood(const size_t inputs, const size_t neurons, const size_t hoodSize)
			: m_Inputs(inputs)
			, m_Neurons(neurons)
			, m_Neighborhood(new double[inputs*neurons])
         , m_HoodSize(hoodSize)
		{
			Initialize();
		}

		~OneDNeighborhood( )
		{
			delete m_Neighborhood;
		}

		size_t Inputs(void) const { return m_Inputs; }
		size_t Neurons(void) const { return m_Neurons; }

		void Get(const neuronid_t nn, double* weights) const;
		double operator() (const neuronid_t nn, const double* weights) const;

		double Update (const neuronid_t nn, const neuronid_t winner, const double* inputs, double eta);
		
		neuronid_t First(void) const { return 0; }
		neuronid_t Last(void) const { return m_Neurons; }

		neuronid_t Next(const neuronid_t nn) const
		{
			return (nn+1);
		}

		bool IsNeighbor(const neuronid_t n1, const neuronid_t n2) const;
		double Weighting(const neuronid_t winner, const neuronid_t node) const;

      size_t GetNeighborhoodSize(void) const { return m_HoodSize; }
      void SetNeighborhoodSize(size_t sz) { m_HoodSize = (sz < 1 ? 1 : sz); }

	private:
		void Initialize(void);

		double*	m_Neighborhood;
		size_t	m_Inputs;
		size_t	m_Neurons;
      size_t   m_HoodSize;
	};
}

#endif /* !defined(_KOHONEN_NETWORK_HPP_) */